package com.doupi.partner.manager.authenticator.realm;

import com.doupi.partner.dal.model.*;
import com.doupi.partner.manager.authenticator.athuc.AutoLoginToken;
import com.doupi.partner.manager.authenticator.athuc.CaptchaToken;
import com.doupi.partner.manager.authenticator.exception.CaptchaInvalidException;
import com.doupi.partner.manager.authenticator.util.PasswordUtils;
import com.doupi.partner.manager.authenticator.util.RedisManagerUtils;
import com.doupi.partner.service.*;
import com.doupi.partner.service.constant.AccountStatusEnum;
import com.doupi.partner.util.AssertUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.SimpleByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 授权realm
 * 
 * @author Super Jens Wang
 * @version 1.0 on 2017/4/3 上午6:12.
 */
public class AuthRealm extends AuthorizingRealm {
	private static final Logger logger = LoggerFactory.getLogger(AuthRealm.class);

	private final AdministratorService administratorService;

	private final AdministratorRoleService administratorRoleService;

	private final AdministratorPermissionService administratorPermissionService;

	private final AdministratorHasRoleService administratorHasRoleService;

	private final AdministratorRoleHasPermissionService administratorRoleHasPermissionService;

	@Autowired
	public AuthRealm(AdministratorService administratorService, AdministratorRoleService administratorRoleService, AdministratorPermissionService administratorPermissionService,
			AdministratorHasRoleService administratorHasRoleService, AdministratorRoleHasPermissionService administratorRoleHasPermissionService) {
		this.administratorService = administratorService;
		this.administratorRoleService = administratorRoleService;
		this.administratorPermissionService = administratorPermissionService;
		this.administratorHasRoleService = administratorHasRoleService;
		this.administratorRoleHasPermissionService = administratorRoleHasPermissionService;
	}

	public AuthRealm() {
		this(null, null, null, null, null);
	}

	/**
	 * 返回授权信息，确定是否可以操作 what can you do
	 * 
	 * The process of access control, i.e. determining ‘who’ has access to ‘what’
	 * 
	 * @param principals
	 *            权限集合
	 * @return 授权信息
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		Administrator administrator = (Administrator) principals.getPrimaryPrincipal();

		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		List<AdministratorHasRole> aToArs;
		AdministratorRole administratorRole;
		List<AdministratorRoleHasPermission> arToAps;
		AdministratorPermission administratorPermission;
		try {
			aToArs = administratorHasRoleService.queryByAdministratorId(administrator.getId());

			for (AdministratorHasRole aToArTemp : aToArs) {
				administratorRole = administratorRoleService.selectByPrimaryKey(aToArTemp.getAdministratorRoleId());
				if (null != administratorRole) {
					simpleAuthorizationInfo.addRole(administratorRole.getCode());
				}

				arToAps = administratorRoleHasPermissionService.queryByAdministratorRoleId(aToArTemp.getAdministratorRoleId());

				for (AdministratorRoleHasPermission permissionTemp : arToAps) {
					administratorPermission = administratorPermissionService.selectByPrimaryKey(permissionTemp.getAdministratorPermissionId());
					if (administratorPermission != null) {
						simpleAuthorizationInfo.addStringPermission(administratorPermission.getCode());
					}
				}
			}

		} catch (Exception e) {
			logger.error("权限出错了", e);
			throw new RuntimeException("权限出错了", e);
		}
		return simpleAuthorizationInfo;
	}

	/**
	 * 判断是否登录，who you are
	 *
	 * Sometimes referred to as ‘login’, this is the act of proving a user is who they say they are
	 *
	 * @param token
	 *            登录的token
	 * @return 登录token信息
	 * @throws AuthenticationException
	 *             认证失败
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 验证码 token
		if (token.getClass().isAssignableFrom(CaptchaToken.class)) {
			CaptchaToken captchaToken = (CaptchaToken) token;
			String cacheCaptcha = RedisManagerUtils.getRedisUtils().get(CaptchaToken.CAPTCHA_CACHE_KEY);
			if (!StringUtils.equalsIgnoreCase(cacheCaptcha, captchaToken.getCaptcha())) { // invalid token
				throw new CaptchaInvalidException(captchaToken.getInvalidMessage());
			}
		}

		Administrator administrator = administratorService.selectByMobile((String) token.getPrincipal());
		if (null == administrator) {
			return null;
		}

		AccountStatusEnum accountStatus = AccountStatusEnum.getAccountStatusByCode(administrator.getStatus());
		AssertUtils.assertNotNull(accountStatus, "账户被锁定", LockedAccountException.class);
		if (accountStatus == AccountStatusEnum.LOCKED) { // 用户被禁用
			throw new LockedAccountException("账户被锁定");
		}

		// 验证码 token
		if (token.getClass().isAssignableFrom(AutoLoginToken.class)) {
			return new SimpleAuthenticationInfo(administrator, PasswordUtils.encryptPassword(AutoLoginToken.DEFAULT_PASSWORD, administrator.getPasswordSalt()),
					new SimpleByteSource(administrator.getPasswordSalt()), administrator.getName());
		}
		return new SimpleAuthenticationInfo(administrator, administrator.getPassword(), new SimpleByteSource(administrator.getPasswordSalt()), administrator.getName());
	}
}
